#include "uart.h"

/// @brief 初始化串口
/// @param brate 
/// @param dev 
UART::UART(int brate, const char *dev)
{
    char* path = new char[strlen(dev) + 1]; // +1 for null terminator
    strcpy(path, dev);
    int fd = this->open_port(path);
    this->set_opt(fd, brate, 8, 'N', 1);
	FD = fd;
	FD_ZERO(&readfds);
    FD_SET(FD, &readfds);
	//std::cout<<"串口" << dev << "初始化完成,波特率为"<<brate<<std::endl;
}

/// @brief 释放串口
UART::~UART()
{
    close(FD);
}

/// @brief 发送数据
/// @param buf 要发送的数据
/// @param buf_len 数据长度
/// @return 
int UART::send(void* buf, int buf_len)
{
	return write(FD, buf, buf_len);
}

/// @brief 接收数据, 阻塞
/// @param buf 接收缓冲区
/// @param bufsize 缓冲区长度
/// @return 
int UART::recv(void* buf, int bufsize)
{
	return read(FD, buf, bufsize);
}

/// @brief 接收数据
/// @param buf  接收缓冲区
/// @param bufsize 缓冲区长度
/// @param timeout 超时时间 timeout.tv_sec 表示秒, timeout.tv_usec 表示微秒
/// @return 超时返回-1
int UART::recv(void* buf, int bufsize, struct timeval timeout)
{
	// 等待串口数据或超时
    int ret = select(FD + 1, &readfds, NULL, NULL, &timeout);
    if (ret == -1) {
        perror("select");
        return -1;
    } else if (ret == 0) {
        printf("Timeout occurred\n");
        return -1;
    }

	return read(FD, buf, bufsize);
}

/// @brief 打开串口
/// @param com 设备号, 例如"/dev/ttyS9"
/// @return 串口文件描述符
int UART::open_port(char *com)
{
    int fd;
	//O_NOCTTY -------- 打开的串口不作为控制终端
    //O_RDWR 表示以读写模式打开文件
	fd = open(com, O_RDWR|O_NOCTTY);
    if (-1 == fd){
		return(-1);
    }
	
    if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
    {
        printf("fcntl failed!\n");
        return -1;
    }
  
    return fd;
}

/// @brief 设置串口
/// @param fd 串口的文件操作符
/// @param nSpeed 串口波特率 2400 4800 9600 115200
/// @param nBits 串口传输的码元长度 一般是 8
/// @param nEvent 校验位 无:N
/// @param nStop 停止位 可选:1,2
/// @return 
int UART::set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) 

{
    struct termios newtio,oldtio;
	//if(ioctl(fd,TCGETS, &oldtio) !=0)
	if ( tcgetattr( fd,&oldtio) != 0) { 
		perror("SetupSerial 1");
		return -1;
	}
	
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag |= CLOCAL | CREAD; 
	newtio.c_cflag &= ~CSIZE; 

	newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
	newtio.c_oflag  &= ~OPOST;   /*Output*/
	//选择传输位数
	switch( nBits )
	{
	case 7:
		newtio.c_cflag |= CS7;
	break;
	case 8:
		newtio.c_cflag |= CS8;
	break;
	}
	//选择校验位
	switch( nEvent )
	{
	case 'O':
		newtio.c_cflag |= PARENB;
		newtio.c_cflag |= PARODD;
		newtio.c_iflag |= (INPCK | ISTRIP);
	break;
	case 'E': 
		newtio.c_iflag |= (INPCK | ISTRIP);
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
	break;
	case 'N': 
		newtio.c_cflag &= ~PARENB;
	break;
	}
	//选择传输速率
	switch( nSpeed )
	{
#define B(x) B##x
#define SET_SPEED(speed) \
    case (speed): \
        cfsetispeed(&newtio, B(speed)); \
        cfsetospeed(&newtio, B(speed)); \
    break;
    
    SET_SPEED(2400)
    SET_SPEED(4800)
    SET_SPEED(9600)
    SET_SPEED(115200)

	default:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
	break;
	}

#undef B
#undef SET_SPEED

	//选择停止位
	if( nStop == 1 )
		newtio.c_cflag &= ~CSTOPB;
	else if ( nStop == 2 )
		newtio.c_cflag |= CSTOPB;
	//设置等待时间
	newtio.c_cc[VMIN]  = 1;  // 读数据时的最小字节数: 没读到这些数据我就不返回!
	newtio.c_cc[VTIME] = 0;  // 等待第1个数据的时间
	//设置阻塞模式
	tcflush(fd,TCIFLUSH);
	//使能配置生效
	//if(ioctl(fd,TCSETS, &newtio) !=0)
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
	return 0;
}


